﻿using System.Collections.Generic;

namespace SharpDepend.Classes.Threading
{
    /// <summary>
    /// Share a dictionary for reading to several threads.
    /// This uses a lock mechanism only when ListHasUpdated() is called. it will do a double buffer copy each time, but will be fast to read from the dincionary from any thread.
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class DictionarySharer<TKey, TValue>
    {
        private IDictionary<TKey, TValue> mList;
        private Dictionary<TKey, TValue> mListForReading1;
        private Dictionary<TKey, TValue> mListForReading2;
        private bool mHasChanged;

        public DictionarySharer(IDictionary<TKey, TValue> list)
        {
            mList = list;

            mListForReading1 = new Dictionary<TKey, TValue>();
            mListForReading2 = new Dictionary<TKey, TValue>();
        }

        /// <summary>
        /// This will copy source list to a secondary list, that can be readed at anytime.
        /// </summary>
        public void ListHasUpdated()
        {
            lock (this)
            {
                mListForReading1.Clear();
                foreach (var item in mList)
                {
                    mListForReading1.Add(item.Key, item.Value);
                }

                mHasChanged = true;
            }
        }

        public TValue this[TKey key]
        {
            get
            {
                if (mHasChanged)
                {
                    lock (this)
                    {
                        UpdateFromOtherThread();
                        mHasChanged = false;
                    }
                }
                return mListForReading2[key];
            }
        }

        public Dictionary<TKey, TValue>.Enumerator GetEnumerator()
        {
            if (mHasChanged)
            {
                lock (this)
                {
                    UpdateFromOtherThread();
                    mHasChanged = false;
                }
            }
            return mListForReading2.GetEnumerator();
        }

        void UpdateFromOtherThread()
        {
            mListForReading2.Clear();
            foreach (var item in mListForReading1)
            {
                mListForReading2.Add(item.Key, item.Value);
            }
        }
    }
}
